perm filename ALINTP.POX[HAL,HE] blob
sn#197472 filedate 1976-01-22 generic text, type C, neo UTF8
COMMENT ā VALID 00014 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00002 00002 \!head2(THE INTERPRETER)
C00004 00003 \!head3(Free Storage)
C00008 00004 \!head3(Graph Structures)
C00015 00005 \!head3(Pseudo-code Interpreter)
C00020 00006 \!head4(Motion Control)
C00023 00007 \!head4(Variables and Expressions)
C00031 00008 \!head4(Stack Operations)
C00035 00009 \!head4(Flow of Control)
C00042 00010 \!head4(Arithmetic)
C00046 00011 \!head4(Condition Monitors)
C00051 00012 \!head4(Force Control)
C00058 00013 \!head4(Initialization)
C00059 00014 \!head4(Debugging Aids)
C00061 ENDMK
Cā;
\!head2(THE INTERPRETER);
\F0\JGlobal communication between the interpreter and the control code is
accomplished through a table (COMTAB) in low core. All global
variables (including procedure addresses) are pointed to in this
table. This permits the control code to be assembled separately from
the interpreter code, and all necessary linking (which is not very
substantial) is then done at execution time. This technique is
necessary because PALX, the assembler we use, does not permit
relocatable modules, nor do we have a linking loader.
The interpreter consists of several submodules. These are the free
storage management scheme, the graph structure routines (designed by
Raphael Finkel and Russell Taylor), and the pseudo-code interpreter
itself.\.
\,
\!head3(Free Storage);
\F0\JFree storage is used for arithmetic values of the several available
datatypes (like transforms), for control structures (like processor
descriptor blocks and interpreter status blocks) and for graph
structure. Currently, 1.5K of free storage is available. There are
two allocation schemes: large block and small block.\.
\JAll blocks are zeroed as they are allocated. Although this
causes extra overhead, it does prevent bugs and avoids the
necessity for numerous explicit clears. Large blocks are allocated
according to the boundary tag method described in Knuth I.2.5. Each
block of storage has a boundary tag at each end, with identical
contents: The number of bytes in the whole area if available, and the
opposite of that if busy. Artificially busy areas are kept above and
below free storage. Large blocks are administered under a
fixed-release policy.\.
\JSmall blocks are used for values and for several other structures.
The basic idea is to break up large blocks of storage into smaller,
fixed size blocks, and then administer them. The small block
allocator provides a facility whereby a user can have a number of
different "spaces" of fixed size blocks. Each space is described by
an approximately 10 word space descriptor. All these space
descriptors are linked together on a big chain (SIDLST), and each
space is assumed to have associated with it a unique 8-bit number
(thus allowing up to 256 spaces). Each space descriptor owns a
linked list of buffers; each buffer contains a number of blocks.
Each space may be either collectable or uncollectable by the garbage
collector. Any block may be released explicitly, although if the
space is collectable, this may be unwise. Also, collectable spaces
are compacted by the garbage collector. As an efficiency measure,
the first 10 space indices are also kept in a table (SIDTBL). When a
new small block is allocated, the free space routine will, if
necessary, call the garbage collector to get more space or (if the
space is not collectable or garbage collection is disabled) it will
call the large block routines to get another buffer. If garbage
collection fails to produce a goodly surplus of blocks for some
space, then additional buffers of new blocks will be obtained.\.
\,
\!head3(Graph Structures);
\F0\JThe graph structure is used for holding variables, expressions, and
the relationships between them. The affixment concept requires that
modification of one variable cause as a programmed side-effect
the modification of other variables. The graph structure implements
the side-effect mechanism.
Variables and expressions are stored in "graph nodes", which are data
structures which have the following fields:\.
\F3\;
GNMODE Mode bits. 1:variable. 2:expression
NXTGN Links all graph nodes. Points to next one.
PRVGN Previous link in that chain
INVMRK 0 => valid, other => invalid
GNVAL points at the value of this graph node
GNDEPS list of dependents (variables or expressions)
\F0\J"Variable nodes" and "expression nodes" are distinguished by the
GNMODE field. Variable nodes also have the following extra fields:\.
\F3\;
VALIDF a count which is incremented at every reevaluation
VNCLCS list of expression nodes used as calculators
VNCHGS list of change cells. (see format below)
\F0Expression nodes have the following extra fields:
\F3\;
ENISB Points to interpreter status block to resolve addressing
ENIPC the interpeter PC where the calculation starts
ENNEED list of needed nodes (cell-linked)
\F0\JIf an expression E is calculated by, say, the two variables X and Y,
then X and Y will appear on the list of needed variables in
E's graph node. E will appear as a dependent in the graph nodes for
X and Y. The IPC of E will point to the pseudo-code necessary to
evaluate the expression E.
The last value calculated for E
will be stored in the value field of E's graph node. If the value of
X or of Y should change, it is necessary to reevaluate E. This is
not done immediately. Instead, all the dependents of the changed
graph node (in this case, E is a dependent) are marked as invalid,
but no attempt is made to reevaluate them. If E itself has
dependents, they would also be marked as invalid.\.
\JE can be given the role of "calculator" for some other variable, say
Z. This is accomplished by placing E on the calculator list for Z
and putting Z on the dependent list for E. Whenever the value of Z
is required, it will be taken from Z's value field if Z is marked as
valid. If not, an attempt will be made to evaluate some calculator
of Z (in this case, E). If E is marked as valid, then its value cell
is used. If not, it is necessary to insure that all the variables on
E's needed list are valid (that is, X and Y) and to render them valid
if necessary. If this succeeds, the pseudo-code for E is interpeted
and the resulting value is stored in E's value cell. Then Z also
places the new value in its value cell.\.
\JCare must be taken to break cycles in the graph structure during the
evaluation of some node. The INVMRK is used to store the "time" of
the current attempt at evaluation, and evaluation fails on any node
which is invalid for the current time.\.
\JThere is no inherent limit on the number of calculators a variable
node may have, nor is there a limit on the number of variables needed
by or dependent on an expression.\.
\JRoutines are available to construct new graph nodes, to destroy old
ones (care is taken to leave the graph structure in a consistent
state), and to make and break the calculator relationship between
variable nodes and expression nodes.\.
\JAnother flavor of programmable side-effect to the modification of
variables is the "changer", or "also-do". It specifies that the
modification of a variable be accompanied by the execution of some AL
statement (which, due to the block structure of AL, can be a sizable
piece of code). This statment has access to two special values,
"old" and "new", which are the value that the variable had before the
assignment and the one it will have after the assignment.\.
\JThe list of changers associated with a variable node is stored in the
field VNCHGS. Each changer has the following structure:\.
\F3\;
NXTCHG next changer cell in chain
CHGISB Points to interpreter status block to resolve addressing
CHGIPC the interpeter PC where the calculation starts
\F0\;
\,
\!head3(Pseudo-code Interpreter);
\JAn interpreter is a process engaged in interpreting
pseudo-code. There may be several active interpreters at any time
(for example, in the execution of a COBEGIN). Each interpreter has a
stack (the "interpreter stack") used to store pointers to
currently "open" variables. During the course of a calculation,
operands and temporary result cells will be open in this fashion. The
interpreter stack is always pointed to by R3. When a new interpreter
is sprouted, it is given a new stack area. Interpreters usually
run at priority 0.
Each procedure has an environment, which is a data area holding
information vital to that procedure. This includes pointers to all
the variables local to that procedure and return information.
Procedures are not yet implemented, so all interpreters have the same
environment. It is possible that blocks will eventually be treated
as procedures without arguments, but this is not currently the case.
Each interpreter has certain status information which facilitates
transfer of control between interpreters. This information is kept
in the interpreter status block, which is always pointed to by R4.
Most important are the IPC, the Interpreter Program Counter, the ENV,
which points to the local environment, and LEV, which stores the
current lexical level.
The fields in the interpreter status block (ISB) are as follows:\.
\F3\;
IPC Interpreter program counter.
NXTINT Next interpreter in the list. For GC of the stacks.
STKBAS Location of start of stack area.
ENV Location of local environment
LEV Lexical level of current execution
STA Status bits for condition codes: 0 means all well.
PDB Location of process descriptor block (for reclamation)
EVT The event to signal as this interpreter goes away
CMCB Pointer to c-m control block if this is a checker or a body
OLDV The "old value" used by changers
NEWV The "new value" used by changers
\F0\;
The environment has certain fixed fields. They are these:
\F3\;
SLINK Pointer to environment of next (outer, lower numbered) block
OLEV Old level. The lexical level of calling process.
OENV Old environment, the one for the calling process.
OIPC Old IPC. Program counter for calling process.
LVARS First location where pointers to local variables go
\F0\;
\JThe pseudo-operations fall into several categories. These will be
treated individually. Arguments to pseudo-instructions are placed directly
after them in the pseudo-code. There are six distinguishable types
of arguments. These will be abbreviated as follows:\.
\F3\;
a absolute address
la list of absolute addresses, terminated by a 0
o offset or level-offset pair
lo list of offsets or level-offset pairs, terminated by a 0
n small constant
r50 word of radix 50 (used to represent three characters)
\F0\;
\,
\!head4(Motion Control);
\;This page modified 1/15/76
\JMotions are prepared by the compiler into trajectory files. These
files specify which joints are to be moved and what the trajectories
are to be.\.
pseudo-op MOVE,<a,n>
\JCreate a device status block for the manipulator code, and call that code
to execute the motion whose table is to be found at absolute location
a. The format of this table is given with the description of the
trajectory calculator elsewhere in this paper.
After the motion, the variables associated with the moving device are
marked invalid, so that future attempts to evaluate them will force
a recalculation. The number n contains the mechanism bits to assist
in finding those variables.
The current
mechanisms are these:\.
\F3\;
1 Yellow arm
2 Yellow hand
4 Blue arm
10 Blue hand
\F0\;
pseudo-op CENTER,<a,n>
\J\
This pseudo-op is very like MOVE, except that the motion table is
much shorter, specifying only the devices (arm and hand) to use.
The centering operation closes the hand while accomodating
the arm to the position of the object between the fingers.
The affected device variables are invalidated, as after a MOVE.
\.
pseudo-op STOP,<n>
\JThe argument n specifies which mechanism(s) to stop.
A table pointed to by the global DVCPTR tells what
device block, if any, is associated with any servo. These device
blocks are modified to indicate that the motion should be stopped.\.
\,
\!head4(Variables and Expressions);
\F0\JVariables and expressions are maintained in the graph structure,
which is discussed elsewhere. Each variable or expression node is
accessed through the environment at some "offset", which is a small
number (currently an even number between 10 and 256).\.
pseudo-op MVAR,<lo>
\JA new variable node is constructed for each element of the list of
arguments, and the environment is updated to include these new
variables at the specified offsets. The new variables are not given
any value, nor do they have any dependents, calculators, or changers.
This operation is usually seen at block entry; all the variables
declared in that block are created.\.
pseudo-op KVAR,<lo>
\JThe variable nodes associated with the offsets in the list are
destroyed. Care is taken to leave the graph structure consistent.
The environment is updated to reflect the fact that these variables
no longer exist. This operation is usually seen at block exit.\.
pseudo-op GLBLNK,<lo,r50,r50>
\JThere is a small list (currently only 10 long) of "global" variables.
These are variables which are global to all interpreters and all
environments. They are intended to be used eventually by the
compiler to pre-assign some values and to facilitate linkage of
separately compiled programs.
The "name" of the global is given in the two words of radix50 (enough
for six characters). The offset where it is to be stored for this
link to it is given by the first argument. If the environment at
this offset already has a pointer, it is assumed that the link has
already been made, so this pseudo-op has no effect. Thus is does not
hurt to repeat it (for example, in a loop). If the environment is
zero at the offset, a search is made through the global table, using the
radix50 name. If it is found, the table will include a pointer to
the proper variable node, and the link is established in the
environment. If it is not found, it is added to the global list, and
the link is established. The first reference to a global therefore
serves to define it.\.
pseudo-op MAKEVT,<lo>
\JEvents are used in AL for process synchronization. This pseudo-op
causes events to be created and established in the environment. The
creation of events is performed by a system call.\.
pseudo-op DESEVT,<lo>
\JRemove the named events from the environment and inform the system
that they are disappearing. Any process waiting on a destroyed event
is informed that the event has disappeared.\.
pseudo-op MEXP,<lo,a,o>
\JMake one new expression node. The list of needed variables is given
in the first (list) argument as a set of level-offsets. These are
resolved into a list of absolute pointers to the variable nodes. The
address a is to be the IPC of the expression node. It is here that
the pseudo-code that accomplishes the calculation is to be found.
(This code should end with the ENDCLC pseudo-code, to be discussed
later.) The new expression is established in the environment at the
offset specified in the third argument.\.
pseudo-op MCLC,<o,o>
\JThe expression whose environment offset is given by the first
argument is attached as a calculator to the variable given by the
second argument.\.
pseudo-op DCLC,<o,o>
\JThe expression whose environment offset is given by the first
argument is detached as a calculator from the variable given by the
second argument.\.
pseudo-op MCHG,<o,a>
\JAdd a changer to the variable specified by the first argument. The
IPC of the pseudo-code for the changer is given in the second
argument. (This code should end with the TERMINATE pseudo-code, to
be discussed later.) There is currently no way to remove a changer
from a variable, short of destroying the variable.\.
pseudo-op GTOLD
\JThis code should only be given by a changer. It causes the old value
of the variable to which this changer is attached to be put on the
interpreter stack. It is found in the ISB, where the graph structure
routines put it before the call to the interpreter that achieves the
change.\.
pseudo-op GTNEW
\JThis code should only be given by a changer. It causes the new value
of the variable to which this changer is attached to be put on the
interpreter stack. It is found in the ISB, where the graph structure
routines put it before the call to the interpreter that achieves the
change.\.
\,
\!head4(Stack Operations);
\F0pseudo-op GTVAL,<o>
\JThe value of the variable specified by the argument is placed on the
interpreter stack. The graph structure routines will get a valid
value if at all possible, using the calculators available if
necessary. Only a pointer to the value is actually placed on the
stack, so any datatype can be treated equally well.\.
pseudo-op IGTVAL,<a>
\JThis is an immediate version of GTVAL. The argument points directly
to the graph node whose value is desired. A pointer to the value
cell is placed on the stack. The absolute address given in the
argument is placed on the stack. This pseudo-op is currently not
used; it is still here for historical reasons.\.
pseudo-op CHNGE,<o>
\JThe value on the top of the interpreter stack is used as a new value
for the variable specified by the argument. The graph structure
change routine is used, and if any changers are associated with this
variable, they will be executed. Any graph nodes dependent on this
variable will be marked as invalid. The value is then cleared from
the interpreter stack.\.
pseudo-op ICHNGE,<a>
\JA pointer to the value on the stack is placed in the graph node
pointed to directly by the argument. This pseudo-op
is an immediate version of CHNGE, and is currently not
used; it is still here for historical reasons.\.
pseudo-op PUSH,<a>
\JThe absolute address given in the argument is placed on the stack.
This pseudo-op is used to access compiler-defined constants.\.
pseudo-op POP
\JThe top element of the interpreter stack is discarded. It is a
pointer to some value cell, and that value is not needed at the
moment.\.
pseudo-op COPY ,<n>
\JThe element n locations down from the top of the stack is copied to
the top of the stack. The instruction COPY 0 duplicates the current
top of the stack.\.
pseudo-op REPLAC,<n>
\JThe element n locations down from the top of the stack is replaced by
the element at the top of the stack. The top is then popped.\.
pseudo-op FLUSH
\JFlush the entire stack.\.
\,
\!head4(Flow of Control);
\F0pseudo-op JUMP, <a>
\JJump to the address specified. Future pseudo-code will be taken from
there.\.
pseudo-op JUMPC,<n,a>
\JThe two arguments are the condition and the destination address. The
value pointed to by the top of the stack is compared (as a scalar) to
zero and is then popped. The interpreter jumps to the destination
address if the condition is satisfied. The possible conditions are
0(Never), 1(L), 2(E), 3(LE), 4(Always), 5(GE), 6(NE), 7(G).
Comparisons of equality must be exact to floating precision.\.
pseudo-op TERMINATE
\JThis interpreter is terminated. The ISB is reclaimed, as is the
process descriptor block. If the EVT field of the ISB is non-zero, then
the event given there is signaled. This is used to inform the graph
structure routines when an interpreter sprouted as a changer has
completed its task. It is also used in conjunction with the SPROUT
code, described below.\.
pseudo-op ENDCLC
\JReturn from a calculator call. The value on the stack (which should
be the value calculated) is placed in R0. The return causes the
graph structure routines to resume execution. This pseudo-op
should only be given from the body of a calculator.\.
pseudo-op PROC,<a,lo>
\JThis code is currently
not fully implemented. It calls a procedure at a, with parameter
list lo. The parameter list for the procedure is made up of pairs.
The first word specifies what variable is meant in the current
environment, and the second tells what the new offset for that
variable should be in the procedure.
The pseudo-code at the destination address should begin with two
words, the first of which tells how many local variables are used by
the procedure (this information is used for allocation), and the
second of which is the lexical level of the procedure (this is used
for resolving references to other variables). Since all parameters
are passed by reference, any value parameters should have first been
copied into local temps which can then be passed by reference.\.
pseudo-op RETURN
\JReturns from a procedure call to calling program. Since variables are
passed by reference, it is not necessary to do any copying of values.
All that is needed is to restore the context of the caller and to
discard the environment.\.
pseudo-op SPROUT,<la>
\JEach address in the list is the IPC of some code. A new interpreter
is created for each address given. A new event is created which will
signal the termination of the new interpreters. Each new
interpreter is given an interpreter status block (the termination
event is stored in EVT in these ISBs) and is then scheduled. As each
terminates, it signals its defining event. Meanwhile, the
interpreter which has executed the SPROUT pseudo-op waits for as many
signals on the event as interpreters it has sprouted. Therefore,
execution will not continue until each of the sprouted interpreters
has finished.\.
pseudo-op FORCHK,<a>
\JThis pseudo-op is used at the head of FOR loops to test if the
termination condition has been reached. Assume that the stack contains,
from the top down, the increment, the final value, and the control
variable's value, all of which are scalar values which define the
state of the FOR loop. If (FINAL-CONVAR)*(INCREMENT) ā„ 0 then this
is a no-op. Otherwise, a jump is made to the destination given in the
argument.\.
pseudo-op SIGNAL,<o>
\JThe event found in the environment at the given level-offset is
signaled. If a process with higher priority is waiting for the
signal, the kernel will pre-empt the current process and run the other
one.\.
pseudo-op WAITE,<o>
\JThe interpreter waits for a signal on the event given by the
level-offset of the argument. If a signal is pending, execution
proceeds immediately. If the failure return should be taken by the
system call EVWAIT, then this interpreter terminates. This however,
should not happen in most programs.\.
\,
\!head4(Arithmetic);
\F0\JAll arithmetic operations expect their arguments on the interpreter
stack, and these arguments are popped before the result is pushed
onto the stack. None of the operations performs type checking on the
arguments, although such checking would be easy to add. The naming
convention for the operations is that the first few letters imply the
datatypes expected for the arguments. S means scalar, V means
vector, P means plane, and T means trans.\.
pseudo-op SADD
a scalar plus a scalar yields a scalar.
pseudo-op SSUB
a scalar minus a scalar yields a scalar.
pseudo-op SNEG
Negation of a scalar yields a scalar.
pseudo-op SMUL
a scalar multiplied by a scalar yields a scalar.
pseudo-op SDIV
a scalar divided by a scalar yields a scalar.
pseudo-op VMAGN
The magnitude of a vector yields a scalar.
pseudo-op VDOT
The dot product of two vectors yields a scalar.
pseudo-op PVDOT
The dot product of a plane with a vector yields a scalar.
pseudo-op SSBRTN,<n>
\JThis calls a scalar subroutine. The argument is the number of the
subroutine.
It is assumed that there is a scalar on the stack, and that the
routine will return a scalar, which is then placed on the stack.
The only routine now available is square root (n=1).\.
pseudo-op VMAKE
A vector is constructed from three scalars.
pseudo-op SVMUL
A scalar multiplied by a vector yields a vector.
pseudo-op VADD
A vector plus a vector yields a vector.
pseudo-op TVMUL
A trans multiplied by a vector yields a vector.
pseudo-op VSAXWR
\JThis is not yet implemented. A trans is made whose rotation is the
rotation of amount given by the scalar about an axis given by the
vector.\.
pseudo-op TMAKE
\JA trans is made from a trans and a vector, which is to be its new
translation vector.\.
pseudo-op TVADD
A trans plus a vector yields a trans.
pseudo-op TTMUL
A trans multiplied by a trans yields a trans.
\;modified 1/15/76
pseudo-op TINVRT
The inverse of a trans is a trans.
\;added 1/15/76
pseudo-op WHERE,<n>
\JThe argument specifies which mechanism is to be located. The current
position of the mechanism (as a trans if it is an arm, and as a scalar
if it is a hand) is placed on the stack.\.
\,
\!head4(Condition Monitors);
\F0\JCondition monitors (abbreviated c-m's) are another type of
programmable side-effect. The graph structure enables one to program
side effects for assignment. Condition monitors allow side effects
to be programmed for three kinds of condition: 1) software: something
which can be tested in the language, such as "A > B", 2) the
signalling of an event, and 3) hardware conditions, such as the
triggering of touch sensors. The current implementation deals only
with the first two types.
The basic operations for c-m's are Creation, Enabling, Disabling, and
Destruction.\.
pseudo-op CMMAK,<o,o,a>
\JCreates a new c-m control block, and points to it in the
environment. The first argument is the offset in the environment,
the second is the level-offset of the event the c-m is to await, if
any, and the third is the address of the c-m code. The c-m control
block has the following fields:\.
\F3\;
CMSEVT The event used to awaken the tester upon enabling
CMTEVT The event for which this c-m tests, if any
CMFORC The FMCB needed, if any, for calculating forces
CMSTAT Status bits for the c-m
CMENB == 1 set => enabled
CMDES == 2 set => to be destroyed
\F0\;
\JThe once-only code of the c-m is sprouted at priority 3 (it is an
interpreter), and after initialization, it waits for the
event CMSEVT.\.
pseudo-op CMENBL,<o>
\JEnabling signals the event CMSEVT and sets the enabled bit in CMSTAT.\.
pseudo-op CMDSBL,<o>
\JDisabling resets the enabled bit, and the c-m will wait on the CMSEVT
for future action.\.
pseudo-op CMDEST,<lo>
\JDestruction sets the CMDES bit and resets the enabled bit. It also
signals CMSEVT to awaken the c-m to the fact that something has
changed.
The following codes should only be executed from the body of a c-m:\.
pseudo-op CMSKED,<n>
\JThis pseudo-op should only be given from inside a c-m. It causes
the c-m to sleep for n milliseconds, and then, if it is still
enabled, to continue its execution. If the c-m is disabled, it
waits on the CMSEVT, which will be signaled when a CMENBL instruction
is executed. If the CMDES bit is on, then the c-m interpreter
terminates, having destroyed the event in CMSEVT and reclaimed the
c-m control block.\.
\JIf this c-m is of the second kind (it waits for some event), the
wait occurs inside the execution of CMSKED. When the event happens,
the status bits are retested. If the monitor is disabled, the event
is resignaled (given back), and once again the c-m waits. If the
event for which it waits is destroyed, the c-m treats this as if the
monitor were destroyed and the CMDES bit were on.\.
pseudo-op CMTRIG
\JThis pseudo-op should only be given from inside a c-m. If the
condition is satisfied in a c-m of the first kind, this pseudo-op
should be executed. It disables the monitor and reduces the priority
to 1. The "body" of the c-m should follow.\.
pseudo-op CMUNCR
\JThis pseudo-op should only be given from inside a c-m. It reduces
the priority to 0, thus entering the "uncritical" region of the c-m
conclusion.\.
\,
\!head4(Force Control);
\F0\JIn order to check the force on a moving mechanism with some
directional component and some moment component, it is necessary to
prepare a "reaction-torque array" which has one number for each
joint. This array depends on the arm configuration and the two
components mentioned above. To calculate the force along those
components, it is then necessary to multiply the current joint reaction
torques by the entries in the reaction torque array, to sum the
resulting products, and to perform a normalization.\.
\JThe reaction-torque array remains usable as long as the arm does not
change its configuration excessively. However, many motions of the
arm are large enough to require several different reaction- torque
arrays to be prepared.\.
\JEach force that is to be monitored (that is, each combination of
direction component, moment component, and mechanism) is governed by
a force monitor control block (FMCB). This control block is
established in the environment. In this sense, a force calculation
is a variable, called a "force variable". The fields in an FMCB are
as follows:\.
\F3\;
FMFOMO Force - moment array. 20 words:
force component in X direction
force component in Y direction
force component in Z direction
(1.0) scaling factor, not used
moment component in X direction
moment component in Y direction
moment component in Z direction
(1.0) scaling factor, not used
FMRETO Reaction - torque array. 14 words.
FMJOAN Joint angle array. 14 words.
FMMECH Arm involved: mechanism bits
FMMODE Mode bits
FMKIL == 2 set if this FM should go away.
FMBEX == 4 set if background job (fills reaction-torque array) exists
FMFEX == 10 set by GETFORCE, reset by MAKRT
\F0\;
pseudo-op MAKFORCE,<o,n>
\JThis code prepares a force variable. The offset is the first
argument, and the mechanism number is the second argument.
A new force monitor block is established in
the environment at that offset. The force-moment array is filled from
the two top elements of the stack, which are then popped: the first
is the force vector, the second is the moment vector. These are both
in hand coordinates. This routine does not load the reaction-torque
array or the joint angle array, but it does point the environment at
the new force variable.\.
pseudo-op DESFORCE,<o>
\JThe force variable is to be destroyed. This is accomplished by
removing the pointer to it in the environment and setting the FMKIL
bit in the FMMODE field of the FMCB. The actual reclamation of the
block will be done by the filler job, to be described below.\.
pseudo-op GETFORCE,<o>
\JThe force for the force variable specified by the argument is
evaluated, and the result is placed on the interpreter stack. This
is accomplished as follows: First, it is necessary that the
reaction-torque array be prepared. There is a background job (the
"filler") that periodically (as currently set, every half second)
fills this array. This job communicates with GETFORCE through the
status bits in FMMODE. If FMBEX is set, then the filler exists and
periodically refreshes the array. Thus GETFORCE checks to see
that the filler exists, and if it does not, the filler is created and
activated at priority 1. GETFORCE implies that the force variable is
still being used by setting the FMFEX bit.\.
\JThe filler, when first called, is given the location of the FMCB in a
register. Every time it awakens, it makes sure that the force is
still needed (that is, that the FMKIL bit is off and the FMFEX is on)
and then sets up the array. It then sleeps for half a second and
tries it again. If the FMKIL is on, then the FMCB and the process
control block are returned to free storage and the process
terminates. If FMFEX is off, then FMBEX is turned off as well, the
PDB is returned to free storage, and the process terminates. Thus if
the force has not been queried for a while (perhaps the GETFORCE is
in a disabled condition monitor), the filler disappears.\.
\,
\!head4(Initialization);
pseudo-op PROG
\J\
This pseudo-op initializes the interpreter to set up variables
for each known mechanism and to link up the appropriate calculators
for those variables. This is accomplished by directly executing
a special piece of pseudo-code containing MVAR, MEXP, and MCLC
for these variables. The calculators use the WHERE operation
to find the current values of the mechanisms.
\.
\,
\!head4(Debugging Aids);
\F0pseudo-op PRINT,<a>
Types the string starting at location a on the terminal.
pseudo-op VALPRN
\JType the top value of the stack, whatever type it is. Then pop it.\.
pseudo-op VARPRN,<o>
\JType the value of the variable given at the specified level-offset,
whatever type it is.\.
pseudo-op NOOP
Null operation.
pseudo-op TOPAL
\JEscape to PAL. The following locations in the pseudo-code are not
interpeted; they are executed. The final instructions in that code
should be:\.
\F3\;
MOV PC,R0
RTS PC
\F0\;